package com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.interper;

import java.io.IOException;
import java.io.StringReader;

import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.VariableAPI.util.strings.splitters.Splitters;
import com.nulldev.util.data.Sets.DataSet;
import com.nulldev.util.data.Sets.DataTriplet;
import com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.BaseJSFunction;
import com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.MissingVariableError;

public final class BaseJSInterperUtils {
	public static final String MATH_OPERATORS_REGEX = "\\+|\\*|\\/|\\-|\\%";

	public static boolean isVariableDecl(final String payload) {
		if (payload == null || payload.isEmpty())
			return false;
		return payload.startsWith("var ") || payload.startsWith("const ") || payload.startsWith("let ");
	}

	public static boolean isDecl(final String payload) { // FIXME: This might be a bad idea.
		if (payload == null || payload.isEmpty())
			return false;
		final String[] o = Splitters.splitString(payload, "=");
		if (o.length <= 1)
			return false;
		return o.length >= 2;
	}

	public static String readValue(final StringReader r) {
		final StringBuffer b = new StringBuffer(48);
		try {
			int c;
			boolean isInFunction = false;
			boolean isInFunctionDecl = false;
			while ((c = r.read()) != -1) {
				if (c == '{' && !isInFunction)
					isInFunction = true;
				if (c == '}' && isInFunction) {
					isInFunction = false;
					isInFunctionDecl = false;
				}
				if (c == ',' && b.toString().substring(0, b.length() - 1).endsWith("function("))
					isInFunctionDecl = true;
				if (c == '{' && b.toString().endsWith(")") && isInFunctionDecl)
					isInFunctionDecl = false;
				if ((c == ',') && !isInFunctionDecl && !isInFunction)
					break;
				b.append((char) c);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return b.toString();
	}

	public static String readUntil(final StringReader r, final char f) {
		final StringBuffer b = new StringBuffer(48);
		try {
			int c;
			while ((c = r.read()) != -1) {
				if (c == f)
					break;
				b.append((char) c);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return b.toString();
	}

	public static String readUntilM(final StringReader r, final char f, final char m) {
		final StringBuffer b = new StringBuffer(48);
		try {
			int c;
			while ((c = r.read()) != -1) {
				if (c == f) {
					r.mark(1);
					final int n = r.read();
					if (n == m)
						break;
					else
						r.reset();
				}
				b.append((char) c);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return b.toString();
	}

	public static String readUntil(final String str, final char f) {
		final StringBuffer b = new StringBuffer(48);
		for (final char c : str.toCharArray()) {
			if (c == f)
				break;
			b.append((char) c);
		}
		return b.toString();
	}

	public static String readFrom(final String str, final char f) {
		final StringBuffer b = new StringBuffer(48);
		boolean start = false;
		for (final char c : str.toCharArray()) {
			if (c != f && !start)
				continue;
			if (c == f && !start) {
				start = true;
				continue;
			}
			b.append((char) c);
		}
		return b.toString();
	}

	public static final char[] MATH_FEEDBACK_ARR = new char[]
		{ '+', '-', '/', '*', '%' };

	public static DataSet<String, Character> readUntilEitherWithFeedback(final StringReader r, final char... fs) {
		final StringBuffer b = new StringBuffer(48);
		int c;
		try {
			boolean isInFunction = false;
			while ((c = r.read()) != -1) {
				if (c == '{' && !isInFunction)
					isInFunction = true;
				if (c == '}' && isInFunction)
					isInFunction = false;
				if (ArrayUtils._contains(fs, (char) c) && !isInFunction)
					break;
				b.append((char) c);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return new DataSet<String, Character>(b.toString(), (char) c);
	}

	public static DataTriplet<String, Character, String> readUntilEitherWithFeedbackT(final StringReader r, final char... fs) {
		final StringBuffer b = new StringBuffer(48);
		int c;
		try {
			boolean isInFunction = false;
			while ((c = r.read()) != -1) {
				if (c == '{' && !isInFunction)
					isInFunction = true;
				if (c == '}' && isInFunction)
					isInFunction = false;
				if (ArrayUtils._contains(fs, (char) c) && !isInFunction)
					break;
				b.append((char) c);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		final char _c = (char) c;
		final String s1 = b.toString();
		b.setLength(0);
		try {
			boolean isInFunction = false;
			while ((c = r.read()) != -1) {
				if (c == '{' && !isInFunction)
					isInFunction = true;
				if (c == '}' && isInFunction)
					isInFunction = false;
				if (ArrayUtils._contains(fs, (char) c) && !isInFunction)
					break;
				b.append((char) c);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return new DataTriplet<String, Character, String>(s1, _c, b.toString());
	}

	public static String readUntilEither(final StringReader r, final char... fs) {
		final StringBuffer b = new StringBuffer(48);
		try {
			int c;
			boolean isInFunction = false;
			while ((c = r.read()) != -1) {
				if (c == '{' && !isInFunction)
					isInFunction = true;
				if (c == '}' && isInFunction)
					isInFunction = false;
				if (ArrayUtils._contains(fs, (char) c) && !isInFunction)
					break;
				b.append((char) c);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return b.toString();
	}

	public static DataSet<String, Character> readUntilEitherWS(final StringReader r, final char... fs) {
		final StringBuffer b = new StringBuffer(48);
		try {
			int c;
			boolean isInFunction = false;
			while ((c = r.read()) != -1) {
				if (c == '{' && !isInFunction)
					isInFunction = true;
				if (c == '}' && isInFunction)
					isInFunction = false;
				if (ArrayUtils._contains(fs, (char) c) && !isInFunction)
					break;
				b.append((char) c);
			}
			return new DataSet<String, Character>(b.toString(), (char) c);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	public static String readUntilEitherAndWithout(final StringReader r, final char[] chars, final char[] exception) {
		final StringBuilder b = new StringBuilder(48);
		try {
			int c;
			while ((c = r.read()) != -1) {
				if (ArrayUtils._contains(chars, (char) c)) {
					break;
				}
				b.append((char) c);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return b.toString();
	}

	public static String readUntilEOF(final StringReader r) {
		final StringBuffer b = new StringBuffer(48);
		try {
			int c;
			while ((c = r.read()) != -1) {
				b.append((char) c);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return b.toString();
	}

	public static boolean isObjectReference(final BaseJSFunction f, final String value) {
		if (f == null)
			return false;
		if (!value.contains("."))
			return false;
		if (value.contains("[") || value.contains("]"))
			return false;
		final String pre = readUntil(value, '.');
		if (f.contains(pre))
			return true;
		if (!f.valueCache.containsKey(pre))
			throw new MissingVariableError("[isObjectReference] Couldn't find: " + pre);
		return true;
	}

	public static boolean isVariabledFunctionReference(final String line) {
		if (line.startsWith("return "))
			return false;
		if (!line.contains("(") && !line.contains(")"))
			return false;
		return line.endsWith(")") && line.contains("=") && !line.contains("==");
	}

	public static boolean isFunctionReference(final String line) {
		if (line.startsWith("return "))
			return false;
		if (!line.contains("(") && !line.contains(")"))
			return false;
		return line.endsWith(")");
	}

	public static String cleanInvalidCharacters(final String in) {
		if (in == null || in.isEmpty())
			return in;
		final StringBuilder out = new StringBuilder(in.length());
		char current;
		if (in == null || ("".equals(in))) {
			return "";
		}
		for (int i = 0; i < in.length(); i++) {
			current = in.charAt(i);
			if ((current == 0x9) || (current == 0xA) || (current == 0xD) || ((current >= 0x20) && (current <= 0xD7FF))
					|| ((current >= 0xE000) && (current <= 0xFFFD)) || ((current >= 0x10000) && (current <= 0x10FFFF))) {
				out.append(current);
			}

		}
		return out.toString().replaceAll("\\s", " ");
	}

	public static boolean isArrayReference(final String value) {
		if (!value.contains("[") && !value.contains("]"))
			return false;
		return value.endsWith("]");
	}

	public static boolean isValue(final String value) {
		if (StringsUtil.isNumber(value))
			return true;
		else if (value.startsWith("\"") || value.endsWith("\""))
			return true;
		else if (value.startsWith("'") || value.endsWith("'"))
			return true;
		else
			return false;
	}

	public static String getVariableFromStatement(final String var) {
		if (isArrayReference(var))
			return readUntil(var, '[');
		return var;
	}

	public static String removeTrailingSpaces(final String str) {
		if (str == null || str.isEmpty())
			return str;
		if (!str.contains(" "))
			return str;
		return str.substring(str.indexOf(" ") + 1);
	}
}
